package com.example.a26;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver;
import org.springframework.web.method.annotation.ModelFactory;
import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.*;

import java.lang.reflect.Method;
import java.util.List;


public class A26Application {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(WebConfig.class);

        RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
        adapter.setApplicationContext(context);
        adapter.afterPropertiesSet();

        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setParameter("name", "张三");

        /*
            现在可以通过 ServletInvocableHandlerMethod 把这些都整合到一起，并完成控制器方法的调用，如下
         */
        ServletInvocableHandlerMethod handlerMethod = new ServletInvocableHandlerMethod(
                new WebConfig.Controller1(), WebConfig.Controller1.class.getMethod("foo", WebConfig.User.class));

        ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
        handlerMethod.setDataBinderFactory(factory); // 数据绑定工厂
        handlerMethod.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer()); // 参数名解析器
        handlerMethod.setHandlerMethodArgumentResolvers(getArgumentResolver(context)); // 设置常用的方法参数解析器

        ModelAndViewContainer container = new ModelAndViewContainer();

        // 获取模型工厂方法
        Method getModelFactory = RequestMappingHandlerAdapter.class.getDeclaredMethod("getModelFactory", HandlerMethod.class, WebDataBinderFactory.class);
        getModelFactory.setAccessible(true);
        ModelFactory modelFactory = (ModelFactory) getModelFactory.invoke(adapter, handlerMethod, factory);

        // 初始化模型数据
        modelFactory.initModel(new ServletWebRequest(request), container, handlerMethod);

        handlerMethod.invokeAndHandle(new ServletWebRequest(request), container);

        System.out.println(container.getModel());

        context.close();
    }

    // 添加常用的参数解析器
    public static HandlerMethodArgumentResolverComposite getArgumentResolver(AnnotationConfigApplicationContext context) {
        HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
        composite.addResolvers(
                new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), false), // 请求参数解析器
                new PathVariableMethodArgumentResolver(), // 路径参数解析器
                new RequestHeaderMethodArgumentResolver(context.getDefaultListableBeanFactory()), // 请求头解析器
                new ServletCookieValueMethodArgumentResolver(context.getDefaultListableBeanFactory()), // cookie 解析器
                new ExpressionValueMethodArgumentResolver(context.getDefaultListableBeanFactory()), // 表达式解析器
                new ServletRequestMethodArgumentResolver(), // 请求方式解析器
                new ServletModelAttributeMethodProcessor(false), // 必须有 @ModelAttribute
                new RequestResponseBodyMethodProcessor(List.of(new MappingJackson2HttpMessageConverter())), // 请求体解析器
                new ServletModelAttributeMethodProcessor(true), // 省略了 @ModelAttribute
                new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), true) // 省略 @RequestParam
        );
        return composite;
    }

}
